home *** CD-ROM | disk | FTP | other *** search
/ SGI Hot Mix 17 / Hot Mix 17.iso / HM17_SGI / research / lib / cw_animate.pro < prev    next >
Text File  |  1997-07-08  |  38KB  |  942 lines

  1. ; $Id: cw_animate.pro,v 1.18 1997/02/04 23:51:55 dave Exp $
  2.  
  3. ;
  4. ; Copyright (c) 1992-1997, Research Systems, Inc.  All rights reserved.
  5. ;    Unauthorized reproduction prohibited.
  6. ;+
  7. ; NAME:
  8. ;    CW_ANIMATE
  9. ;
  10. ; PURPOSE:
  11. ;    This widget displays an animated sequence of images using
  12. ;    X-windows Pixmaps. This is a compound widget, based on the
  13. ;    XINTERANIMATE procedure, with the following advantages:
  14. ;        - It can be included in other applications.
  15. ;        - Multiple copies can be run simultaneously.
  16. ;
  17. ;    The speed and direction of the display can be adjusted using
  18. ;    the widget interface.
  19. ;
  20. ; CATEGORY:
  21. ;    Image display, compound widgets.
  22. ;
  23. ; CALLING SEQUENCE:
  24. ;    To initially create:
  25. ;        widget = CW_ANIMATE(PARENT, SIZEX, SIZEY, NFRAMES)
  26. ;
  27. ;    To reinitialize when another animation is loaded:
  28. ;        CW_ANIMATE_INIT, ANIMATEBASE, SIZEX, SIZEY, NFRAMES
  29. ;
  30. ;    To load a single image:
  31. ;        CW_ANIMATE_LOAD, WIDGET, IMAGE = IMAGE, FRAME = FRAME_INDEX
  32. ;
  33. ;    To load a single image that is already displayed in an existing window:
  34. ;
  35. ;        CW_ANIMATE_LOAD, WIDGET, FRAME = FRAME_INDEX, $
  36. ;            WINDOW = [WINDOW_NUMBER [, X0, Y0, SX, SY]]
  37. ;
  38. ;    (This technique is much faster than reading back from the window.)
  39. ;
  40. ;    To display the animation after all the images have been loaded:
  41. ;
  42. ;        CW_ANIMATE, WIDGET [, RATE]
  43. ;
  44. ;    To get a copy of the vector of Pixmaps being used by the widget.
  45. ;    If this routine is called, the widget does not destroy the pixmaps
  46. ;    when it is destroyed. The user can then provide them to a later
  47. ;    call to CW_ANIMATE to re-use them while skipping the Pixmap creation
  48. ;    and rendering step:
  49. ;
  50. ;        CW_ANIMATE_GETP, widget, PIXMAPS
  51. ;
  52. ; INPUTS:
  53. ;   CW_ANIMATE:
  54. ;        PARENT:     The ID of the parent widget.
  55. ;        SIZEX:     The width of the displayed image.
  56. ;        SIZEY:     The height of the displayed image.
  57. ;        NFRAMES: The number of frames in the animation sequence.
  58. ;
  59. ;   CW_ANIMATE_INIT:
  60. ;        ANIMATEBASE: The ID of the base animation widget.
  61. ;        SIZEX:     The width of the displayed image.
  62. ;        SIZEY:     The height of the displayed image.
  63. ;        NFRAMES: The number of frames in the animation sequence.
  64. ;
  65. ;      CW_ANIMATE_LOAD:
  66. ;        WIDGET:     The ID of the widget (as created with CW_ANIMATE)
  67. ;             into which the image should be loaded.
  68. ;
  69. ;   CW_ANIMATE_RUN:
  70. ;        WIDGET:     The ID of the widget (as created with CW_ANIMATE)
  71. ;             into which the image should be loaded.
  72. ;        RATE:     A value between 0 and 100 that represents the
  73. ;             speed of the animation as a percentage of the
  74. ;             maximum display rate. The fastest animation has
  75. ;             a value of 100 and the slowest  has a value of 0.
  76. ;             The default animation rate is 100.
  77. ;       STOP:    If this keyword is set, the animation is stopped.
  78. ;       NFRAMES: Specifies the number of frames to animate, must
  79. ;                        <= the number specified in CW_ANIMATE().
  80. ;
  81. ; KEYWORD PARAMETERS:
  82. ;       CW_ANIMATE:
  83. ;        PIXMAPS: This keyword provides the new widget with a vector
  84. ;             of pre-existing pixmap (off screen window) IDs.
  85. ;             This vector is usually obtained from a call to
  86. ;             CW_ANIMATE_GETP applied to a previous animation
  87. ;             widget.
  88. ;        UVALUE:  A user supplied value to be stored in the widget's
  89. ;             user value field.
  90. ;               NO_KILL: If NOT set, an "End Animation" button is added to the
  91. ;             animation base.  If set the button is not added.
  92. ;        OPEN_FUNC: A user supplied string that specifies a callback
  93. ;             function name. When a value is specified for this
  94. ;             keyword, an "Open..." pushbutton is added to the
  95. ;             window.  When the "Open..." pushbutton is clicked
  96. ;             the OPEN_FUNC function is called to load new
  97. ;             animation data.
  98. ;        INFO_FILE: A filename containing text to be displayed by
  99. ;                        XDISPLAYFILE when user selects the help button.
  100. ;
  101. ;       CW_ANIMATE_INIT:
  102. ;        PIXMAPS: This keyword provides the new widget with a vector
  103. ;             of pre-existing pixmap (off screen window) IDs.
  104. ;             This vector is usually obtained from a call to
  105. ;             CW_ANIMATE_GETP applied to a previous animation
  106. ;             widget.
  107. ;
  108. ;       CW_ANIMATE_LOAD:
  109. ;        CYCLE:   If set, cycle. Normally, frames are displayed
  110. ;             going either forward or backwards. If CYCLE is
  111. ;             set, reverse direction after the last frame in
  112. ;             either direction is displayed.
  113. ;        FRAME:      The frame number to be loaded. This is a value
  114. ;             between 0 and NFRAMES. If not supplied, frame 0
  115. ;               is loaded.
  116. ;        IMAGE:   The image to be loaded.
  117. ;        ORDER:   Set this keyword to display images from the top
  118. ;             down instead of the default bottom up. This keyword
  119. ;             is only used when loading images with the IMAGE
  120. ;             keyword.
  121. ;        TRACK:   If set, the frame slider tracks the current frame.
  122. ;             Default is not to track.
  123. ;        WINDOW:  When this keyword is specified, an image is copied
  124. ;             from an existing window to the animation pixmap.
  125. ;             When using X windows, this technique is much faster
  126. ;             than reading from the display and then loading with
  127. ;             the IMAGE keyword.
  128. ;
  129. ;             The value of this parameter is either an IDL window
  130. ;             number (in which case the entire window is copied),
  131. ;             or a vector containing the window index and the
  132. ;             rectangular bounds of the area to be copied. For
  133. ;             example:
  134. ;             WINDOW = [Window_Number, X0, Y0, Sx, Sy]
  135. ;
  136. ;              XOFFSET: The horizontal offset, in pixels from the left of
  137. ;             the frame, of the image in the destination window.
  138. ;
  139. ;              YOFFSET: The vertical offset, in pixels from the bottom of
  140. ;             the frame, of the image in the destination window.
  141. ;
  142. ; OUTPUTS:
  143. ;    No explicit outputs.
  144. ;
  145. ; SIDE EFFECTS:
  146. ;    If the widget is realized before calls to CW_ANIMATE_LOAD, the frames
  147. ;    are displayed as they are loaded. This provides the user with an
  148. ;    indication of how things are progressing.
  149. ;
  150. ;    When the widget is destroyed, it destroys the pixmaps used in the
  151. ;    animation, unless they were previously obtained via CW_ANIMATE_GETP
  152. ;       and the KILL_ANYWAY keyword was not set.
  153. ;
  154. ;    The only event returned by this widget indicates that the user
  155. ;    has pressed the DONE button. The parent application should use
  156. ;    this as a signal to kill the animation widget via WIDGET_CONTROL.
  157. ;
  158. ; RESTRICTIONS:
  159. ;    If more than one animation widget is running at a time, they
  160. ;    will fight for resources and run slower.
  161. ;
  162. ; PROCEDURE:
  163. ;    When initialized, this procedure creates pixmaps containing the
  164. ;    frames of the animation sequence. Once the images are loaded,
  165. ;    they are displayed by copying the images from the pixmap or buffer
  166. ;    to the visible draw widget.
  167. ;
  168. ; EXAMPLE:
  169. ;    Assume the following event handler procedure exists:
  170. ;        PRO EHANDLER, EV
  171. ;          WIDGET_CONTROL, /DESTROY, EV.TOP
  172. ;        end
  173. ;
  174. ;    Enter the following commands to open the file ABNORM.DAT (a series
  175. ;    of images of a human heart) and load the images it contains into
  176. ;    an array H:
  177. ;
  178. ;        OPENR, 1, FILEPATH('abnorm.dat', SUBDIR = 'images')
  179. ;        H = BYTARR(64, 64, 16)
  180. ;        READU, 1, H
  181. ;        CLOSE, 1
  182. ;        H = REBIN(H, 128, 128, 16)
  183. ;
  184. ;    Create an instance of the animation widget at load the frames:
  185. ;
  186. ;        base = widget_base()
  187. ;        animate = CW_ANIMATE(base, 128, 128, 16)
  188. ;        WIDGET_CONTROL, /REALIZE, base
  189. ;        for i=0,15 do CW_ANIMATE_LOAD, animate, FRAME=i, IMAGE=H(*,*,I)
  190. ;
  191. ;    Start the animation:
  192. ;
  193. ;        CW_ANIMATE_RUN, animate
  194. ;        XMANAGER, "CW_ANIMATE Demo", base, EVENT_HANDLER = "EHANDLER"
  195. ;
  196. ;    Pressing the DONE button kills the application.
  197. ;
  198. ; MODIFICATION HISTORY:
  199. ;    AB, June 1992        Heavily based on the XINTERANIMATE procedure.
  200. ;    SR, September 1992    Fixed a problem when a paused animation's
  201. ;                frame selection was moved and the resulting
  202. ;                frame change ended up in another animation.
  203. ;    SR, November  1992    Fixed a problem when a single paused animation
  204. ;                would fail when the frame selection slider
  205. ;                event tried to set do a bad drawing window.
  206. ;    DMS/AB, March, 1993    Got rid of state caching. Got rid of
  207. ;                XMANAGER background tasks in favor of new
  208. ;                "WIDGET_CONTROL,timer=" feature.
  209. ;    ACY, October 1993    Set RETAIN=2 for draw widget to prevent
  210. ;                clipping by an overlapping window when
  211. ;                loading frames.
  212. ;       DMS, Dec, 1993          Added STOP and NFRAMES keywords to CW_ANIMATE_RUN.
  213. ;                               Added KILL_ANYWAY keyword to CW_ANIMATE_GETP.
  214. ;       WSO, Jan, 1995          Added OPEN_FUNC keyword and updated UI.
  215. ;       ACY, Jan, 1997          Added INFO_FILE keyword to allow user-supplied
  216. ;                               files for help text
  217. ;-
  218.  
  219. PRO SetBitmapButtons, state
  220.  
  221.   COMMON BitmapButtons, $
  222.              reversebutton, blk_reversebutton, $
  223.              pausebutton, blk_pausebutton, $
  224.              playbutton, blk_playbutton, $
  225.              cycleForwardBtn, blk_cycleForwardBtn
  226.  
  227.   WIDGET_CONTROL, state.currentAction, SET_VALUE = state.currentBitmap
  228.  
  229.   IF state.framedelta EQ 0 THEN BEGIN  ; paused
  230.      WIDGET_CONTROL, state.wPauseButton, SET_VALUE = blk_pausebutton
  231.      state.currentAction = state.wPauseButton
  232.      state.currentBitmap = pausebutton
  233.   ENDIF ELSE BEGIN
  234.      IF state.framedelta GT 0 THEN BEGIN  ; animating forward
  235.         IF state.cycle THEN BEGIN
  236.            WIDGET_CONTROL, state.wCyclePlayButton, SET_VALUE = blk_cycleForwardBtn
  237.            state.currentAction = state.wCyclePlayButton
  238.            state.currentBitmap = cycleForwardBtn
  239.         ENDIF ELSE BEGIN
  240.            WIDGET_CONTROL, state.wPlayButton, SET_VALUE = blk_playbutton
  241.            state.currentAction = state.wPlayButton
  242.            state.currentBitmap = playbutton
  243.         ENDELSE
  244.      ENDIF ELSE BEGIN                       ; animating backwards
  245.         WIDGET_CONTROL, state.wReversePlayButton, SET_VALUE = blk_reversebutton
  246.         state.currentAction = state.wReversePlayButton
  247.         state.currentBitmap = reversebutton
  248.      ENDELSE
  249.   ENDELSE
  250. END
  251.  
  252.  
  253. PRO CW_ANIMATE_CLN, widget
  254. ; When the widget dies, clean up here. Widget is the ID of the
  255. ; *child* actually holding the state in its UVALUE.
  256.  
  257.      ; kills state stored in widget
  258.    WIDGET_CONTROL, widget, GET_UVALUE = state, /NO_COPY
  259.  
  260.    IF (N_ELEMENTS(state) GT 0) THEN BEGIN
  261.       IF (state.dont_kill_pixmaps EQ 0) THEN BEGIN
  262.          pwin = *state.pwinHdl 
  263.          FOR i=0, N_ELEMENTS(pwin)-1 DO BEGIN
  264.             IF (pwin[i] GE 0) THEN $
  265.                WDELETE, pwin[i]
  266.          ENDFOR
  267.          PTR_FREE, state.pwinHdl
  268.       ENDIF
  269.         ; Restore the state
  270.       WIDGET_CONTROL, widget, SET_UVALUE = state, /NO_COPY
  271.    ENDIF
  272. END
  273.  
  274.  
  275.  
  276.  
  277.  
  278. FUNCTION CW_ANIMATE_EV, event
  279.  
  280.      ; Retrieve the structure from the child that contains the sub ids
  281.    wAnimateBase = event.handler
  282.    wTopBase = WIDGET_INFO(wAnimateBase, /CHILD)
  283.     ;This kills the old uvalue
  284.    WIDGET_CONTROL, wTopBase, GET_UVALUE = state, /NO_COPY
  285.    ret = 0
  286.  
  287.    CASE event.id OF
  288.       wAnimateBase: $
  289.          IF (state.framedelta NE 0) THEN BEGIN            ; Animation
  290.             WIDGET_CONTROL, wAnimateBase, TIMER=state.delay
  291.             curframe = state.curframe
  292.             nframes = state.nframes
  293.  
  294.             curframe = curframe + state.framedelta            ; New frame
  295.  
  296.             r = 0.0
  297.             IF state.cycle THEN BEGIN
  298.                IF curframe LT 0 THEN BEGIN
  299.                   state.framedelta = 1
  300.                   curframe = 0
  301.                   t = systime(1)
  302.                   r = 2 * nframes / float(t-state.loop_start_t)
  303.                ENDIF
  304.                IF curframe GE nframes THEN BEGIN
  305.                   state.framedelta = -1
  306.                   curframe = nframes-1
  307.                ENDIF
  308.             ENDIF ELSE BEGIN
  309.                WHILE curframe LT 0 DO $
  310.                   curframe = curframe + nframes ; Into range
  311.                WHILE curframe GE nframes DO $
  312.                   curframe = curframe - nframes
  313.                IF curframe EQ 0 THEN BEGIN                  ; Display rate?
  314.                   t = systime(1)
  315.                   r = nframes / FLOAT(t-state.loop_start_t)  ; Rate in Frames/Sec
  316.                ENDIF
  317.             ENDELSE
  318.  
  319.             state.curframe = curframe
  320.             IF r NE 0.0 THEN BEGIN            ;Update time?
  321.                WIDGET_CONTROL, state.wFramesPerSecValue, SET_VALUE = $
  322.                   STRING(r, FORMAT='(f6.1)')
  323.                state.loop_start_t = t
  324.             ENDIF
  325.         SWIN = !d.window
  326.             WSET, state.draw_win                  ;Set to the drawing window
  327.             pwin = Temporary(*state.pwinHdl)
  328.             IF pwin[curframe] GE 0 THEN $            ;Next frame
  329.                DEVICE, COPY =[0, 0, state.sizex, state.sizey, 0, 0, pwin[curframe]]
  330.             IF state.track THEN $
  331.                WIDGET_CONTROL, state.wFramesIndicatorSlider, SET_VALUE = curframe
  332.             *state.pwinHdl = Temporary(pwin)
  333.             EMPTY
  334.         WSET, SWIN
  335.          ENDIF
  336.  
  337.       state.wFramesSpeedSlider : BEGIN            ;New rate
  338.          WIDGET_CONTROL, state.wFramesSpeedSlider, GET_VALUE = temp
  339.          IF temp EQ 100 THEN $
  340.             state.delay=0. $
  341.          ELSE $
  342.             state.delay= 2./(1.+temp)
  343.          END
  344.  
  345.       state.wFramesIndicatorSlider : BEGIN
  346.          WIDGET_CONTROL, state.wFramesIndicatorSlider, GET_VALUE = temp
  347.          IF (temp NE state.curframe) THEN BEGIN
  348.         SWIN = !d.window
  349.             WSET, state.draw_win
  350.             state.curframe = temp
  351.             pwin = Temporary(*state.pwinHdl)
  352.             IF (pwin[temp] GE 0) THEN $
  353.                DEVICE, COPY = [0, 0, state.sizex, state.sizey, 0, 0, pwin[temp]]
  354.             *state.pwinHdl = Temporary(pwin)
  355.             EMPTY
  356.         WSET, SWIN
  357.          ENDIF
  358.          END
  359.  
  360.       state.wPauseButton : $
  361.          IF (state.framedelta NE 0) THEN BEGIN
  362.             WIDGET_CONTROL, state.wFramesIndicatorSlider, SET_VALUE = state.curframe
  363.             WIDGET_CONTROL, state.wFramesIndicatorSlider, SENSITIVE = 1
  364.             WIDGET_CONTROL, state.wFramesPerSecValue, SET_VALUE = $
  365.                   STRING(0.0, FORMAT='(f6.1)')
  366.             state.framedelta = 0
  367.             SetBitmapButtons, state
  368.          ENDIF
  369.  
  370.       state.wPlayButton : BEGIN
  371.          WIDGET_CONTROL, state.wFramesIndicatorSlider, SENSITIVE = 0
  372.          WIDGET_CONTROL, state.wFramesSpeedSlider, SENSITIVE = 1
  373.          IF (state.framedelta EQ 0) THEN $
  374.             WIDGET_CONTROL, wAnimateBase, TIMER=state.delay
  375.          state.framedelta = 1
  376.          state.cycle = 0
  377.          SetBitmapButtons, state
  378.          END
  379.  
  380.       state.wReversePlayButton : BEGIN
  381.          WIDGET_CONTROL, state.wFramesIndicatorSlider, SENSITIVE = 0
  382.          WIDGET_CONTROL, state.wFramesSpeedSlider, SENSITIVE = 1
  383.          IF (state.framedelta EQ 0) THEN $
  384.             WIDGET_CONTROL, wAnimateBase, TIMER=state.delay
  385.          state.framedelta = -1
  386.          state.cycle = 0
  387.          SetBitmapButtons, state
  388.          END
  389.  
  390.       state.wCyclePlayButton : BEGIN
  391.          WIDGET_CONTROL, state.wFramesIndicatorSlider, SENSITIVE = 0
  392.          WIDGET_CONTROL, state.wFramesSpeedSlider, SENSITIVE = 1
  393.          IF (state.framedelta EQ 0) THEN $
  394.             WIDGET_CONTROL, wAnimateBase, TIMER=state.delay
  395.          state.framedelta = 1
  396.          state.cycle = 1
  397.          SetBitmapButtons, state
  398.          END
  399.  
  400.       state.wActiveSliderCheck: BEGIN
  401.          state.track = event.select
  402.          END
  403.  
  404.       state.wHelpButton : BEGIN
  405.            ;  If the information file is given, display this file.
  406.            ;  Otherwise, display the text shown below.
  407.            IF (state.InfoFile NE '') THEN BEGIN
  408.               XDisplayFile, state.InfoFile, $
  409.                  DONE_BUTTON='Done', $
  410.                  Title="Animation Help", $
  411.                  Group=event.top, Width=55, Height=16
  412.            ENDIF ELSE BEGIN
  413.                XDISPLAYFILE, "animatedemo.hlp", TITLE = "Animation Help", $
  414.                  GROUP = event.top, WIDTH = 55, HEIGHT = 16, $
  415.                  TEXT = [ $
  416.                   "                                                       ", $
  417.                   "      The animation widget is used for displaying", $
  418.                   "a series of images created with IDL as an animation.  The", $
  419.                   "user can select the speed, direction or specific frames in", $
  420.                   "the animation.", $
  421.                   "      The top slider is used to control the speed of", $
  422.                   "the animation.  Moving it to the far right is one hundred", $
  423.                   "percent, as fast as the animation can go.  If there are", $
  424.                   "other IDL widget applications using background tasks,", $
  425.                   "they can slow down the animation.  Closing the other", $
  426.                   "applications can speed up the animation.", $
  427.                   "      The four bitmap buttons are reverse play, pause, ", $
  428.                   "forward play and cycle.  Use them to select a direction or to", $
  429.                   "pause the animation and view specific framestate.", $
  430.                   "      The bottom slider is used to view single frames", $
  431.                   "from the animation.  The animation must be paused to ", $
  432.                   "use the frame selection slider."   ]
  433.             ENDELSE
  434.          END     
  435.       state.wColorsButton : $
  436.          XLOADCT, GROUP = event.top
  437.  
  438.       state.wOpenButton : BEGIN
  439.            ; get a copy before the structure is killed
  440.          open_func = state.open_func
  441.          framedelta = state.framedelta
  442.          wFramesSpeedSlider = state.wFramesSpeedSlider
  443.            ; Need to restore state since the following routines use it
  444.          WIDGET_CONTROL, wTopBase, SET_UVALUE = state, /NO_COPY
  445.          CW_ANIMATE_RUN, wAnimateBase, /STOP
  446.            ; Disable all controls until all frames are loaded
  447.          WIDGET_CONTROL, wAnimateBase, SENSITIVE = 0
  448.          fileOK = CALL_FUNCTION(open_func, event.top, wAnimateBase)
  449.  
  450.          IF fileOK THEN BEGIN
  451.             WIDGET_CONTROL, wFramesSpeedSlider, GET_VALUE = rate
  452.             IF framedelta EQ 0 THEN $
  453.                framedelta = 1
  454.             CW_ANIMATE_RUN, wAnimateBase, rate, DELTA=framedelta, /LASTFRAME
  455.          ENDIF ELSE $
  456.               ; Disable all controls until all frames are loaded
  457.             WIDGET_CONTROL, wAnimateBase, SENSITIVE = 1
  458.  
  459.  
  460.            ; Need structure back - This kills the old uvalue
  461.          WIDGET_CONTROL, wTopBase, GET_UVALUE = state, /NO_COPY
  462.          END
  463.  
  464.       state.wEndAnimationButton: $
  465.          ret = {ID:wAnimateBase, TOP:event.top, HANDLER:0L, action:"DONE" }
  466.  
  467.       ELSE:
  468.       ENDCASE
  469.  
  470.    WIDGET_CONTROL, wTopBase, SET_UVALUE = state, /NO_COPY   ; Restore the state
  471.    RETURN, ret
  472.  
  473. END
  474.  
  475.  
  476.  
  477.  
  478.  
  479. pro CW_ANIMATE_LOAD, widget, IMAGE = image, FRAME = frame, ORDER = order, $
  480.       WINDOW = window, XOFFSET = xoffset, YOFFSET = yoffset, $
  481.       TRACK = track, CYCLE = cycle
  482.  
  483.  
  484.    wTopBase = WIDGET_INFO(widget, /CHILD)
  485.    WIDGET_CONTROL, wTopBase, GET_UVALUE = state, /NO_COPY
  486.    pwin = Temporary(*state.pwinHdl)
  487.  
  488.    old_window = !D.WINDOW
  489.  
  490.    displayload = WIDGET_INFO(widget, /REALIZED)
  491.    IF (displayload NE 0) THEN BEGIN
  492.       WIDGET_CONTROL, GET_VALUE=temp, state.wImageArea
  493.       state.draw_win = temp
  494.       WSET, state.draw_win
  495.       ; In case the draw widget size is different than that requested.
  496.       state.sizex = !D.X_VSIZE
  497.       state.sizey = !D.Y_VSIZE
  498.    ENDIF
  499.  
  500.    ; Default values and range checking
  501.    IF (N_ELEMENTS(yoffset) EQ 0) THEN $
  502.       yoffset = 0
  503.    IF (N_ELEMENTS(xoffset) EQ 0) THEN $
  504.       xoffset = 0
  505.    IF (N_ELEMENTS(frame)) GT 0 THEN BEGIN
  506.       IF (frame LT 0) OR (frame GE N_ELEMENTS(pwin)) THEN $
  507.          MESSAGE, "Frame number must be from 0 to nframes -1."
  508.    ENDIF ELSE $
  509.       frame=0
  510.  
  511.  
  512.    j = N_ELEMENTS(window)            ;check to see if WINDOW was set
  513.  
  514.    IF (j GT 0) THEN BEGIN            ;Copy image from window?
  515.       IF (j LT 5) THEN BEGIN            ;If coords not spec, use all
  516.          WSET, window[0]
  517.          p = [ window[0], 0, 0, !D.X_VSIZE, !D.Y_VSIZE ]  ;Get size of window
  518.       ENDIF ELSE $
  519.          p = window
  520.  
  521.       IF pwin[frame] LT 0 THEN BEGIN            ;Create pixwin?
  522.          WINDOW, /FREE, XSIZE = state.sizex, YSIZE = state.sizey, /PIXMAP
  523.          pwin[frame] = !D.WINDOW
  524.       ENDIF
  525.  
  526.       IF (p[3] GT state.sizex) OR (p[4] GT state.sizey) THEN $
  527.          MESSAGE, "Window parameter larger than setup"
  528.  
  529.       IF displayload THEN BEGIN            ;Load display window
  530.          WSET, state.draw_win                        ;Show it?
  531.          IF state.draw_win NE p[0] THEN $            ;Copy to show window?
  532.             DEVICE, COPY = [ p[1], p[2], p[3], p[4], xoffset, yoffset, p[0]]
  533.          WSET, pwin[frame]                  ;Pixmap destination
  534.             ;Copy from display window to pixmap
  535.          DEVICE, COPY = [ xoffset, yoffset, p[3], p[4], xoffset, yoffset, $
  536.             state.draw_win ]
  537.       ENDIF ELSE BEGIN                  ;load / no show
  538.          WSET, pwin[frame]
  539.          DEVICE, COPY = [ p[1], p[2], p[3], p[4], xoffset, yoffset, p[0]]
  540.       ENDELSE
  541.  
  542.       EMPTY
  543.       IF (N_ELEMENTS(state.draw_win) EQ 0) THEN $
  544.          state.draw_win = -1
  545.  
  546.       WSET, old_window
  547.  
  548.        ; When displayload is set, the frame slider should update to show frame num
  549.       IF displayload THEN $
  550.          WIDGET_CONTROL, state.wFramesIndicatorSlider, SET_VALUE = frame
  551.       GOTO, Done
  552.    ENDIF                                    ;So WINDOW was not set.
  553.  
  554.    IF N_ELEMENTS(image) NE 0  THEN BEGIN            ;Make sure image was set.
  555.  
  556.        ; When displayload is set, the draw widget should be updated
  557.        ; to show the new frame being loaded and the frame slider
  558.        ; should be set correspondingly
  559.       IF (displayload NE 0) THEN BEGIN
  560.          WIDGET_CONTROL, state.wFramesIndicatorSlider, SET_VALUE = frame
  561.          WSET, state.draw_win
  562.          TV, image
  563.          EMPTY
  564.       ENDIF
  565.  
  566.        ;Make sure the image is of a valid size and report if not.
  567.       sz = SIZE(image)
  568.       IF ((sz[0] NE 2) OR (sz[1] GT state.sizex) OR (sz[2] GT state.sizey)) THEN $
  569.          MESSAGE, "Image parameter must be 2D of size" + $
  570.                   STRING(state.sizex)+ STRING(state.sizey)
  571.  
  572.       IF N_ELEMENTS(order) EQ 0 THEN $
  573.          ORDER = 0        ;Default order
  574.       IF pwin[frame] LT 0 THEN BEGIN
  575.          WINDOW, /FREE, xsize = state.sizex, ysize = state.sizey, /pixmap
  576.          pwin[frame] = !D.WINDOW
  577.       ENDIF ELSE $
  578.          WSET, pwin[frame]
  579.       TV, image, xoffset, yoffset, ORDER = order
  580.       EMPTY
  581.       WSET, old_window
  582.       GOTO, Done
  583.    ENDIF                                    ;End of "if IMAGE was set".
  584.  
  585.    Done: *state.pwinHdl = Temporary(pwin)
  586.  
  587.    WIDGET_CONTROL, wTopBase, SET_UVALUE = state, /NO_COPY  ;Restore uvalue
  588.  
  589. END ;CW_ANIMATE_LOAD
  590.  
  591.  
  592.  
  593.  
  594. pro CW_ANIMATE_RUN, widget, rate, STOP = stop, NFRAMES = nframes, $
  595.                        DELTA = delta, LASTFRAME=lastFrame
  596.  
  597.    wTopBase = WIDGET_INFO(widget, /CHILD)
  598.    WIDGET_CONTROL, wTopBase, GET_UVALUE = state, /NO_COPY
  599.  
  600.    old_window = !D.WINDOW                        ;Save old window
  601.  
  602.    ; Refuse to run if the cluster isn't realized.
  603.    IF (WIDGET_INFO(widget, /REALIZED) EQ 0) THEN $
  604.       MESSAGE,'Animation widget must be realized before it can run'
  605.  
  606.    IF KEYWORD_SET(stop) THEN BEGIN           ;Stop the animation
  607.       state.framedelta = 0                ;This shows we've stopped.
  608.       WIDGET_CONTROL, state.wFramesIndicatorSlider, SET_VALUE = state.curframe
  609.       WIDGET_CONTROL, state.wFramesIndicatorSlider, SENSITIVE = 1
  610.       SetBitmapButtons, state
  611.       GOTO, done
  612.    ENDIF
  613.  
  614.    ; It is realized now, so get the draw widget window ID.
  615.    WIDGET_CONTROL, GET_VALUE=temp, state.wImageArea
  616.    state.draw_win = temp
  617.    WSET, temp
  618.    EMPTY
  619.  
  620.    WIDGET_CONTROL, widget, /SENSITIVE
  621.    WIDGET_CONTROL, state.wFramesIndicatorSlider, SENSITIVE = 0
  622.    IF N_ELEMENTS(nframes) GT 0 THEN BEGIN        ;Nframes spec?
  623.       pwin = Temporary(*state.pwinHdl)
  624.       IF nframes GT N_ELEMENTS(pwin) THEN $
  625.          MESSAGE, 'Run called with too many frames'
  626.       *state.pwinHdl = Temporary(pwin)
  627.       state.nframes = nframes
  628.    ENDIF
  629.  
  630.  
  631.    ;Set up the initial values used by the background task
  632.    IF N_ELEMENTS(lastFrame) NE 0 THEN $
  633.       state.curframe = state.nframes $
  634.    ELSE $
  635.       state.curframe = 0
  636.  
  637.    IF N_ELEMENTS(delta) NE 0 THEN $
  638.       state.framedelta = -1 > delta < 1 $           ; In range?
  639.    ELSE $
  640.       state.framedelta = 1
  641.  
  642.    IF N_ELEMENTS(rate) NE 0 THEN BEGIN
  643.       rate = 0 > rate < 100            ; In range?
  644.       WIDGET_CONTROL, state.wFramesSpeedSlider, SET_VALUE = rate
  645.    ENDIF ELSE $
  646.       rate = 100
  647.  
  648.    IF rate EQ 100 THEN $
  649.       state.delay=0.0 $
  650.    ELSE $
  651.       state.delay = 2./(1.+rate)
  652.  
  653.    SetBitmapButtons, state
  654.  
  655.    state.loop_start_t = SYSTIME(1)            ;Start of loop time
  656.    WIDGET_CONTROL, widget, TIMER=state.delay, EVENT_FUNC = 'CW_ANIMATE_EV'
  657.  done:
  658.    WIDGET_CONTROL, wTopBase, SET_UVALUE = state, /NO_COPY      ;Rewrite state
  659.  
  660.    WSET, old_window
  661. END
  662.  
  663.  
  664. pro CW_ANIMATE_GETP, widget, PIXMAPS, KILL_ANYWAY = kill_anyway
  665. ; Return the vector of pixmap ID's associated with the animation
  666. ; widget in named variable PIXMAPSTATE. Frames without a pixmap contain a -1.
  667. ; This routine should not be called until all the frames are loaded, or the
  668. ; vector will not be complete. It should be called before the call to
  669. ; CW_ANIMATE_RUN.
  670. ;
  671. ; Note: Normally, the animation widget destroys its pixmaps when it
  672. ;      is destroyed. If this routine is called however, the pixmaps
  673. ;      are not destroyed. Cleanup becomes the responsibility of the
  674. ;      caller.
  675.    wTopBase = WIDGET_INFO(widget, /CHILD)
  676.    WIDGET_CONTROL, wTopBase, GET_UVALUE = state, /NO_COPY
  677.    pwin = Temporary(*state.pwinHdl)
  678.    pixmaps = pwin
  679.    *state.pwinHdl = Temporary(pwin)
  680.    IF KEYWORD_SET(kill_anyway) EQ 0 THEN $
  681.       state.dont_kill_pixmaps = 1
  682.    WIDGET_CONTROL, wTopBase, SET_UVALUE = state, /NO_COPY
  683. END
  684.  
  685.  
  686. PRO CW_ANIMATE_INIT, wAnimateBase, sizex, sizey, nframes, PIXMAPS=old_pixmaps
  687.  
  688.    ON_ERROR, 2                                    ;return to caller
  689.  
  690.    wTopBase = WIDGET_INFO(wAnimateBase, /CHILD)
  691.    WIDGET_CONTROL, wTopBase, GET_UVALUE = state, /NO_COPY
  692.  
  693.    nparams = N_PARAMS()
  694.    IF (nparams LT 3) OR (nparams GT 4) THEN $
  695.       MESSAGE, 'Incorrect number of arguments'
  696.    IF NOT (KEYWORD_SET(uval)) THEN $
  697.       uval = 0
  698.  
  699.    n = N_ELEMENTS(old_pixmaps)
  700.    IF (n GT 0) THEN BEGIN
  701.       nframes = n
  702.       pwin = old_pixmaps
  703.    ENDIF ELSE $
  704.       pwin = REPLICATE(-1, nframes)            ;Array of window indices
  705.  
  706.    IF (nframes LE 1) THEN $
  707.       MESSAGE, "Animations must have 2 or more frames"
  708.  
  709.        ; save the number of frames to animate in the animation structure
  710.    state.nframes = nframes
  711.        ; save the Pixmap array to animate in the animation structure
  712.    IF PTR_VALID(state.pwinHdl) THEN BEGIN
  713.        ; Need to temporarily restore state since the following routine uses it
  714.       WIDGET_CONTROL, wTopBase, SET_UVALUE = state, /NO_COPY
  715.       CW_ANIMATE_CLN, wTopBase
  716.        ; Need structure back - This kills the widget uvalue
  717.       WIDGET_CONTROL, wTopBase, GET_UVALUE = state, /NO_COPY
  718.    ENDIF
  719.  
  720.    state.pwinHdl = PTR_NEW(pwin, /NO_COPY)
  721.  
  722.    WIDGET_CONTROL, state.wFramesIndicatorSlider, SET_SLIDER_MAX = nframes - 1
  723.  
  724.    IF state.wImageArea NE 0 THEN BEGIN
  725.         ; to avoid flash - only set the size if it changes
  726.       IF (state.sizex NE sizex OR state.sizey NE sizey) THEN $
  727.          WIDGET_CONTROL, state.wImageArea, XSIZE =sizex, YSIZE=sizey
  728.    ENDIF ELSE BEGIN
  729.       wImageBase = WIDGET_BASE(wTopBase, /COLUMN)      ;To prevent stretching
  730.       state.wImageArea = WIDGET_DRAW(wImageBase, XSIZE =sizex, YSIZE=sizey, $
  731.                          XOFFSET = 280, YOFFSET = 20, RETAIN = 2)
  732.    ENDELSE
  733.  
  734.        ; save the x dimensions of draw widget in the animation structure
  735.    state.sizex = sizex
  736.        ; save the y dimensions of draw widget in the animation structure
  737.    state.sizey = sizey
  738.  
  739.        ; Disable all controls until all frames are loaded
  740.    WIDGET_CONTROL, wAnimateBase, SENSITIVE = 0
  741.  
  742.    WIDGET_CONTROL, wTopBase, SET_UVALUE = state, /NO_COPY
  743.  
  744. END
  745.  
  746.  
  747. ; Setup the play reverse, pause, play forward and cycle pushbutton bitmaps. These
  748. ; variables reside in the "BitmapButtons" common block.
  749. ; Both a depressed (blk_) and a not-depressed version are needed for each button.
  750. PRO InitBitmapButtons
  751.  
  752.   COMMON BitmapButtons
  753.  
  754.    reversebutton = [[000B, 000B, 000B], [000B, 032B, 000B], [000B, 048B, 000B],$
  755.         [000B, 056B, 000B], [000B, 060B, 000B], [000B, 046B, 000B], $
  756.         [000B, 231B, 015B], [144B, 003B, 024B], [016B, 231B, 027B], $
  757.         [080B, 238B, 027B], [208B, 060B, 026B], [208B, 056B, 026B], $
  758.         [208B, 048B, 026B], [208B, 032B, 026B], [208B, 000B, 026B], $
  759.         [208B, 000B, 026B], [208B, 255B, 027B], [016B, 000B, 024B], $
  760.         [240B, 255B, 031B], [224B, 255B, 015B], [000B, 000B, 000B], $
  761.         [000B, 000B, 000B], [000B, 000B, 000B], [000B, 000B, 000B] ]
  762.    blk_reversebutton = [[255B, 255B, 255B], [255B, 223B, 255B],[255B, 207B, 255B],$
  763.         [255B, 199B, 255B], [255B, 195B, 255B], [255B, 209B, 255B], $
  764.         [255B, 024B, 240B], [111B, 252B, 231B], [239B, 024B, 228B], $
  765.         [175B, 017B, 228B], [047B, 195B, 229B], [047B, 199B, 229B], $
  766.         [047B, 207B, 229B], [047B, 223B, 229B], [047B, 255B, 229B], $
  767.         [047B, 255B, 229B], [047B, 000B, 228B], [239B, 255B, 231B], $
  768.         [015B, 000B, 224B], [031B, 000B, 240B], [255B, 255B, 255B], $
  769.         [255B, 255B, 255B], [255B, 255B, 255B], [255B, 255B, 255B] ]
  770.    pausebutton = [[000B, 000B, 000B], [000B, 000B, 000B], [000B, 000B, 000B], $
  771.         [192B, 195B, 003B], [192B, 194B, 002B], [192B, 194B, 002B], $
  772.         [192B, 194B, 002B], [192B, 194B, 002B], [192B, 194B, 002B], $
  773.         [192B, 194B, 002B], [192B, 194B, 002B], [192B, 194B, 002B], $
  774.         [192B, 194B, 002B], [192B, 194B, 002B], [192B, 194B, 002B], $
  775.         [192B, 194B, 002B], [192B, 194B, 002B], [192B, 194B, 002B], $
  776.         [192B, 194B, 002B], [192B, 195B, 003B], [192B, 195B, 003B], $
  777.         [000B, 000B, 000B], [000B, 000B, 000B], [000B, 000B, 000B] ]
  778.    blk_pausebutton = [[255B, 255B, 255B], [255B, 255B, 255B], [255B, 255B, 255B],$
  779.         [063B, 060B, 252B], [063B, 061B, 253B], [063B, 061B, 253B], $
  780.         [063B, 061B, 253B], [063B, 061B, 253B], [063B, 061B, 253B], $
  781.         [063B, 061B, 253B], [063B, 061B, 253B], [063B, 061B, 253B], $
  782.         [063B, 061B, 253B], [063B, 061B, 253B], [063B, 061B, 253B], $
  783.         [063B, 061B, 253B], [063B, 061B, 253B], [063B, 061B, 253B], $
  784.         [063B, 061B, 253B], [063B, 060B, 252B], [063B, 060B, 252B], $
  785.         [255B, 255B, 255B], [255B, 255B, 255B], [255B, 255B, 255B] ]
  786.    playbutton = [[000B, 000B, 000B], [000B, 004B, 000B], [000B, 012B, 000B], $
  787.         [000B, 028B, 000B], [000B, 060B, 000B], [000B, 116B, 000B], $
  788.         [240B, 231B, 000B], [024B, 192B, 009B], [216B, 231B, 008B], $
  789.         [216B, 119B, 010B], [088B, 060B, 011B], [088B, 028B, 011B], $
  790.         [088B, 012B, 011B], [088B, 004B, 011B], [088B, 000B, 011B], $
  791.         [088B, 000B, 011B], [216B, 255B, 011B], [024B, 000B, 008B], $
  792.         [248B, 255B, 015B], [240B, 255B, 007B], [000B, 000B, 000B], $
  793.         [000B, 000B, 000B], [000B, 000B, 000B], [000B, 000B, 000B] ]
  794.    blk_playbutton = [[255B, 255B, 255B], [255B, 251B, 255B], [255B, 243B, 255B],$
  795.         [255B, 227B, 255B], [255B, 195B, 255B], [255B, 139B, 255B], $
  796.         [015B, 024B, 255B], [231B, 063B, 246B], [039B, 024B, 247B], $
  797.         [039B, 136B, 245B], [167B, 195B, 244B], [167B, 227B, 244B], $
  798.         [167B, 243B, 244B], [167B, 251B, 244B], [167B, 255B, 244B], $
  799.         [167B, 255B, 244B], [039B, 000B, 244B], [231B, 255B, 247B], $
  800.         [007B, 000B, 240B], [015B, 000B, 248B], [255B, 255B, 255B], $
  801.         [255B, 255B, 255B], [255B, 255B, 255B], [255B, 255B, 255B] ]
  802.   cycleForwardBtn = [[000B, 000B, 000B], [000B, 000B, 000B], [000B, 128B, 000B], $
  803.         [000B, 128B, 001B], [000B, 128B, 003B], [248B, 255B, 006B], $
  804.         [008B, 000B, 012B], [008B, 000B, 024B], [248B, 255B, 012B], $
  805.         [248B, 255B, 006B], [000B, 128B, 003B], [000B, 129B, 001B], $
  806.         [128B, 129B, 000B], [192B, 001B, 000B], [096B, 255B, 015B], $
  807.         [048B, 000B, 008B], [024B, 000B, 008B], [048B, 255B, 015B], $
  808.         [096B, 255B, 015B], [192B, 001B, 000B], [128B, 001B, 000B], $
  809.         [000B, 001B, 000B], [000B, 000B, 000B], [000B, 000B, 000B] ]
  810.    blk_cycleForwardBtn = [[255B, 255B, 255B], [255B, 255B, 255B], [255B, 127B, 255B], $
  811.         [255B, 127B, 254B], [255B, 127B, 252B], [007B, 000B, 249B], $
  812.         [247B, 255B, 243B], [247B, 255B, 231B], [007B, 000B, 243B], $
  813.         [007B, 000B, 249B], [255B, 127B, 252B], [255B, 126B, 254B], $
  814.         [127B, 126B, 255B], [063B, 254B, 255B], [159B, 000B, 240B], $
  815.         [207B, 255B, 247B], [231B, 255B, 247B], [207B, 000B, 240B], $
  816.         [159B, 000B, 240B], [063B, 254B, 255B], [127B, 254B, 255B], $
  817.         [255B, 254B, 255B], [255B, 255B, 255B], [255B, 255B, 255B] ]
  818. END
  819.  
  820.  
  821. function CW_ANIMATE, parent, sizex, sizey, nframes, $
  822.     UVALUE=uval, $
  823.     PIXMAPS=old_pixmaps, TRACK = track, CYCLE=cycle, DRAW = draw, $
  824.     NO_KILL = no_kill, OPEN_FUNC=open_func, $
  825.     INFO_FILE= infoFile
  826.  
  827.   COMMON BitmapButtons
  828.  
  829.    ON_ERROR, 2                                    ;return to caller
  830.  
  831.       ; Set the bitmaps for the bitmap buttons
  832.    InitBitmapButtons
  833.  
  834.    nparams = N_PARAMS()
  835.    IF (nparams LT 3) OR (nparams GT 4) THEN $
  836.       MESSAGE, 'Incorrect number of arguments'
  837.  
  838.    IF NOT (KEYWORD_SET(infoFile))  THEN $
  839.         infoFile = ''
  840.    IF NOT (KEYWORD_SET(uval))  THEN $
  841.       uval = 0
  842.    IF NOT (KEYWORD_SET(open_func)) THEN $
  843.       open_func = 0
  844.  
  845.    wAnimateBase = WIDGET_BASE(parent, /COLUMN)
  846.    wTopBase = WIDGET_BASE(wAnimateBase, /ROW)
  847.    wControlBase = WIDGET_BASE(wTopBase, /COLUMN, /FRAME); XPAD=10, YPAD=10, SPACE=20)
  848.    wVCRButtonBase = WIDGET_BASE(wControlBase, /ROW)
  849.    wReversePlayButton = WIDGET_BUTTON(wVCRButtonBase, VALUE = reversebutton)
  850.    wPauseButton = WIDGET_BUTTON(wVCRButtonBase, VALUE = blk_pausebutton)
  851.    wPlayButton = WIDGET_BUTTON(wVCRButtonBase, VALUE = playbutton)
  852.    wCyclePlayButton = WIDGET_BUTTON(wVCRButtonBase, VALUE = cycleForwardBtn)
  853.    currentAction = wPauseButton
  854.    currentBitmap = pausebutton
  855.  
  856.    wSpeedBase = WIDGET_BASE(wControlBase, /COLUMN)
  857.    wSpeedBaseLabel = WIDGET_LABEL(wSpeedBase, VALUE = "Animation Speed:", $
  858.                                   /ALIGN_LEFT)
  859.    wFramesSpeedBase = WIDGET_BASE(wSpeedBase, TITLE = "Animation Speed", /COLUMN, /FRAME)
  860.    wFramesPerSecBase = WIDGET_BASE(wFramesSpeedBase, /ROW)
  861.    wFramesPerSecLabel = WIDGET_LABEL(wFramesPerSecBase, VALUE = "Frames/Sec:")
  862.    wFramesPerSecValue = WIDGET_LABEL(wFramesPerSecBase, VALUE = '0.000')
  863.    wFramesSpeedSlider = WIDGET_SLIDER(wFramesSpeedBase, /DRAG, VALUE = 100, $
  864.              MAXIMUM = 100, MINIMUM = 0, /SUPPRESS_VALUE)
  865.  
  866.    wFrameBase = WIDGET_BASE(wControlBase, /COLUMN)
  867.    wFrameBaseLabel = WIDGET_LABEL(wFrameBase, VALUE = "Animation Frame:", $
  868.                                   /ALIGN_LEFT)
  869.    wFrameIndicatorBase = WIDGET_BASE(wFrameBase, TITLE = "Animation Frame", $
  870.                                      /COLUMN, /FRAME)
  871.    wFramesIndicatorSlider = WIDGET_SLIDER(wFrameIndicatorBase, /DRAG, VALUE = 0, $
  872.              MAXIMUM = nframes - 1, MINIMUM = 0)
  873.    wActiveSliderCheck = CW_BGROUP(wFrameIndicatorBase, ['Active Slider'], $
  874.            FRAME = 0, /NONEXCLUSIVE, /RETURN_INDEX, $
  875.            SET_VALUE=KEYWORD_SET(track))
  876.  
  877.    wButtonBase = WIDGET_BASE(wControlBase, /COLUMN, /ALIGN_LEFT)
  878.    IF KEYWORD_SET(no_kill) THEN $
  879.       wEndAnimationButton = 0L $
  880.    ELSE $
  881.       wEndAnimationButton = WIDGET_BUTTON(wButtonBase, VALUE='End Animation')
  882.    wColorsButton = WIDGET_BUTTON(wButtonBase, VALUE='Colors...')
  883.    IF (KEYWORD_SET(open_func)) THEN $
  884.       wOpenButton = WIDGET_BUTTON(wButtonBase, VALUE='Open...') $
  885.    ELSE $
  886.       wOpenButton = 0
  887.    wHelpButton = WIDGET_BUTTON(wButtonBase, VALUE='Help')
  888.  
  889.    IF N_ELEMENTS(draw) EQ 1 THEN $
  890.       wImageArea = draw $
  891.    ELSE $
  892.       wImageArea = 0
  893.  
  894.    ; Set the event handler function. This cluster does not get or set a value
  895.    ; Make sure it lingers so the cleanup routine can get at its state.
  896.    WIDGET_CONTROL, wAnimateBase, SET_UVALUE = uval, EVENT_FUNC = 'CW_ANIMATE_EV', $
  897.        /DELAY_DESTROY
  898.  
  899.    ;pwin = REPLICATE(-1, nframes)            ;Array of window indices
  900.  
  901.    ; This structure gets stuffed into the uval. of the first child
  902.    ; of wAnimateBase
  903.    WIDGET_CONTROL, wTopBase, SET_UVALUE = $
  904.       { wEndAnimationButton: wEndAnimationButton, $      ; End button
  905.         wColorsButton: wColorsButton, $                  ; Adjust color palette button
  906.         wOpenButton: wOpenButton, $                       ; Open file button
  907.         open_func: open_func, $                          ; Open file function
  908.         wHelpButton: wHelpButton, $                      ; Help button
  909.         wActiveSliderCheck: wActiveSliderCheck, $        ; button group widget
  910.         wFramesSpeedSlider: wFramesSpeedSlider, $        ; Speed selection slider
  911.         wReversePlayButton: wReversePlayButton, $        ; Reverse button
  912.         wPauseButton: wPauseButton, $                    ; Stop (pause) button
  913.         wPlayButton: wPlayButton, $                      ; Forward button
  914.         wCyclePlayButton: wCyclePlayButton, $            ; Cycle forward button
  915.         currentAction : currentAction, $                 ; current action button id
  916.         currentBitmap : currentBitmap, $                 ; current button bitmap
  917.         wFramesIndicatorSlider: wFramesIndicatorSlider, $; Frame selection slider
  918.         wFramesPerSecValue: wFramesPerSecValue, $        ; Animation rate display
  919.         wImageArea: wImageArea, $                        ; Draw widget for animation
  920.         draw_win: -1, $                                  ; Window # of draw widget
  921.         sizex: 0, sizey: 0, $                            ; Dimensions of draw widget
  922.         nframes: 0, $                                    ; # of frames in animation
  923.         curframe: 0, $
  924.         cycle: KEYWORD_SET(cycle), $                     ; Ne 0 to cycle
  925.         track: KEYWORD_SET(track), $                     ; Ne 0 to track with slider
  926.         framedelta: 0, $                                 ; # frames to step.
  927.         delay: 0.0D, $                                   ; Delay between frames
  928.         loop_start_t: 0.0D, $                            ; System time at start
  929.         dont_kill_pixmaps: 0, $                          ; TRUE if pixmaps preserved on kill
  930.         infoFile: infoFile, $                            ; Information text file name
  931.         pwinHdl: PTR_NEW() }                             ; handle to the Pixmap array
  932.  
  933.   ; When the child holding the state gets killed, have a cleanup
  934.   ; procedure called to mop up
  935.    WIDGET_CONTROL, wTopBase, KILL_NOTIFY = 'CW_ANIMATE_CLN'
  936.  
  937.    CW_ANIMATE_INIT, wAnimateBase, sizex, sizey, nframes, PIXMAPS=old_pixmaps
  938.  
  939.    RETURN, wAnimateBase
  940.  
  941. END
  942.